#include <stdio.h>
#include <assert.h>
#include "mystring.h"
#include "Codification.h"
#include "StackOperations.h"
#include "stack.h"
#include <stddef.h>
#define ERROR 0

double* ComToArray();

typedef struct
{
    struct stack_t st;
    double ax;
    double bx;
    double cx;
    double dx;
} proc;


int main()
{
    char filename[100] = {};
    fscanf("%100s", filename);
    double* Commands = ComToArray(filename);
    struct stack_t stack;
    struct stack_t calls;
    Stack_ctor(&calls, Commands[0]);
    if (!Stack_ctor(&stack, Commands[0])) assert(ERROR == stackerror(Stack_errno));
    proc corei7 = {stack, POISON, POISON, POISON, POISON};
    double length = Commands[0];
    double* labels = Commands;
    Commands = Commands + 10;
    for (int i = 0; i < length; i++)
    {
        switch ((int)Commands[i])
        {
            case ADD:
                {
                    add(&corei7.st);
                    break;
                }
            case MUL:
                {
                    mul(&corei7.st);
                    break;
                }
            case SUB:
                {
                    sub(&corei7.st);
                    break;
                }
            case DIV:
                {
                    struct_div(&corei7.st);
                    break;
                }
            case SQRT:
                {
                    stack_sqrt(&corei7.st);
                    break;
                }
            case SQR:
                {
                    sqr(&corei7.st);
                    break;
                }
            case PUSH:
                {
                    i++;
                    Stack_push(&corei7.st, Commands[i]);
                    break;
                }
            case POPAX:
                {
                    Stack_pop(&corei7.st, &corei7.ax);
                    break;
                }
            case PUSHAX:
                {
                    Stack_push(&corei7.st, corei7.ax);
                    break;
                }
            case POPBX:
                {
                    Stack_pop(&corei7.st, &corei7.bx);
                    break;
                }
            case PUSHBX:
                {
                    Stack_push(&corei7.st, corei7.bx);
                    break;
                }
            case POPCX:
                {
                    Stack_pop(&corei7.st, &corei7.cx);
                    break;
                }
            case PUSHCX:
                {
                    Stack_push(&corei7.st, corei7.cx);
                    break;
                }
            case POPDX:
                {
                    Stack_pop(&corei7.st, &corei7.dx);
                    break;
                }
            case PUSHDX:
                {
                    Stack_push(&corei7.st, corei7.dx);
                    break;
                }
            case JB:
                {
                    double a = 0, b = 0;
                    int x = Stack_pop(&corei7.st, &a);
                    int y = Stack_pop(&corei7.st, &b);
                    if (x && y)
                    {
                        if (a < b)
                        {
                            i++;
                            int GoTo = (int)labels[(int)Commands[i]];
                            i = GoTo - 1;
                        }
                        else i++;
                    }
                    else if (x)
                    {
                        Stack_push(&corei7.st, a);
                    }
                    break;
                }
            case JBE:
                {
                    double a = 0, b = 0;
                    int x = Stack_pop(&corei7.st, &a);
                    int y = Stack_pop(&corei7.st, &b);
                    if (x && y)
                    {
                        if (a <= b)
                        {
                            i++;
                            int GoTo = (int)labels[(int)Commands[i]];
                            i = GoTo - 1;
                        }
                        else i++;
                    }
                    else if (x)
                    {
                        Stack_push(&corei7.st, a);
                    }
                    break;
                }
            case JA:
                {
                    double a = 0, b = 0;
                    int x = Stack_pop(&corei7.st, &a);
                    int y = Stack_pop(&corei7.st, &b);
                    if (x && y)
                    {
                        if (a > b)
                        {
                            i++;
                            int GoTo = (int)labels[(int)Commands[i]];
                            i = GoTo - 1;
                        }
                        else i++;
                    }
                    else if (x)
                    {
                        Stack_push(&corei7.st, a);
                    }
                    break;
                }
            case JAE:
                {
                    double a = 0, b = 0;
                    int x = Stack_pop(&corei7.st, &a);
                    int y = Stack_pop(&corei7.st, &b);
                    if (x && y)
                    {
                        if (a >= b)
                        {
                            i++;
                            int GoTo = (int)labels[(int)Commands[i]];
                            i = GoTo - 1;
                        }
                        else i++;
                    }
                    else if (x)
                    {
                        Stack_push(&corei7.st, a);
                    }
                    break;
                }
            case JE:
                {
                    double a = 0, b = 0;
                    int x = Stack_pop(&corei7.st, &a);
                    int y = Stack_pop(&corei7.st, &b);
                    if (x && y)
                    {
                        if (a == b)
                        {
                            i++;
                            int GoTo = (int)labels[(int)Commands[i]];
                            i = GoTo - 1;
                        }
                        else i++;
                    }
                    else if (x)
                    {
                        Stack_push(&corei7.st, a);
                    }
                    break;
                }
            case JNE:
                {
                    double a = 0, b = 0;
                    int x = Stack_pop(&corei7.st, &a);
                    int y = Stack_pop(&corei7.st, &b);
                    if (x && y)
                    {
                        if (a != b)
                        {
                            i++;
                            int GoTo = (int)labels[(int)Commands[i]];
                            i = GoTo - 1;
                        }
                        else i++;
                    }
                    else if (x)
                    {
                        Stack_push(&corei7.st, a);
                    }
                    break;
                }
            case OUT:
                {
                    i = length;
                    break;
                }
            case CALL:
                {
                    Stack_push(&calls, i);
                    i++;
                    int GoTo = (int)labels[(int)Commands[i]];
                    i = GoTo - 1;
                    break;
                }
            case RET:
                {
                    double x = 0;
                    Stack_pop(&calls, &x);
                    i = (int) x;
                    break;
                }
            default:
                {
                    break;
                }
        }
    }
    free(labels);
    if (!strcmp(filename, "asmquadratic.txt"))
    {
        int flag = 1;
        for (int i = 0; i < 3; i++)
        {
            if (corei7.st.Array[i] != i) flag = 0;
        }
        if (flag) printf("%s", "All reals are roots");
        else
        {
            printf("Number of roots: %d\n", corei7.st.Position);
            if (corei7.st.Position > 0) printf("x1 = %lg\n", corei7.st.Array[0]);
            if (corei7.st.Position == 2) printf("x2 = %lg\n", corei7.st.Array[1]);
        }
    }
    if (!strcmp(filename, "asmFib.txt"))
    {
        printf("The %lgth Fibonacci number is %lg\n", corei7.dx, corei7.st.Array[0]);
    }
    Stack_dtor(&corei7.st);
}

double* ComToArray(char* filename)
{
    FILE* source = fopen(filename, "r");
    assert(source);
    int length = 0;
    int labels[10] = {};
    while(1)
    {
        double x = 0;
        if(fscanf(source, "%lg", &x))
        {
            if (x == PUSH)
            {
                fscanf(source, "%lg", &x);
                length += 2;
            }
            else if (x == JB || x == JA || x == JE || x == JB || x == JBE || x == JAE || x == JNE || x == JUMP || x == CALL)
            {
                fscanf(source, " :%lg", &x);
                length += 2;
            }
            else if (x == 0) break;
            else length ++;
        }
        else
        {
            int y = POISON;
            fscanf(source, ":%d", &y);
            if (y != POISON)
            {
                assert(y < 10);
                labels[y] = length;
            }
        }
    }
    fclose(source);
    source = fopen(filename, "r");
    assert(source);
    double* result = (double*) calloc(length + 10, sizeof(double));
    assert(result);
    result[0] = length;
    for (int i = 1; i < 10; i++)
    {
        result[i] = labels[i];
    }
    for (int i = 10; i < length + 10; i++)
    {
        double x = 0;
        if (fscanf(source, "%lg", &result[i]))
        {
            if (result[i] == PUSH)
            {
                i++;
                fscanf(source, "%lg", &result[i]);
            }
            else if (result[i] == JA || result[i] == JE || result[i] == JB || result[i] == JBE || result[i] == JAE || result[i] == JNE || result[i] == JUMP || result[i] == CALL)
            {
                i++;
                fscanf(source, " :%lg", &result[i]);
            }
        }
        else
        {
            fscanf(source, ":%lg", &x);
            i--;
        }
    }
    fclose(source);
    return result;
}
